import logging
import time
from typing import Any, Dict
from app.core.exceptions import RobotException, ErrorCode, ScanGatewayException
from app.services.driver import paramiko_driver
from typing import Optional, Tuple
import datetime
import uuid
from app.services.robot import FlexClientHandler, FlexHttpClientHardware
from app.services.robot.discover_flex import scan_flex
from typing import Callable
from functools import wraps
from app.core import exceptions, error_code
from shared_data.robot import Mount, Point

logger = logging.getLogger("app.services.robot")


def catch_exception(detail=""):
    def catch_exception_func(func: Callable):
        @wraps(func)
        async def wrapper(*args, **kwargs):
            try:
                await func(*args, **kwargs)
            except exceptions.FlexHTTPClientException:
                raise
            except Exception as e:
                raise exceptions.FlexHandlerException(
                    error_code=error_code.ErrorCode.UNKNOWN_ERROR, detail=f"{detail}, {e}")
        return wrapper

    return catch_exception_func


class FlexHandler(paramiko_driver.ParamikoDriver):
    def __init__(self):
        super().__init__()
        self.clients: Dict[str, FlexClientHandler] = {}

    def __del__(self):
        self.close()

    @catch_exception(detail="Update date fail")
    def update_date(self, date: Optional[str] = None) -> Tuple[str, str]:
        """
        更新OT3上的时间
        :param date:
        :return:
        """
        # 先获取当前的时间
        old_date = self.execute_command("date")
        if old_date.status:
            old_date_str = old_date.output
        else:
            raise RobotException(error_code=ErrorCode.PARAMIKO_EXECUTE_FAIL, detail="Update date fail")
        if date is None:
            # 获取当前时间
            current_time = datetime.datetime.now()
            # 格式化为 Linux date 命令需要的格式
            date = current_time.strftime("%Y-%m-%d %H:%M:%S")
        get_date = self.execute_command(f"mount -o remount ,rw / && timedatectl set-ntp false && "
                                        f"timedatectl set-time '{date}'")

        if get_date.status:
            new_date = self.execute_command("date")
            return old_date_str, new_date.output
        else:
            raise RobotException(error_code=ErrorCode.PARAMIKO_EXECUTE_FAIL,
                                 detail="Update date fail \r\n" + get_date.error)

    @catch_exception(detail="Recover date fail")
    def recover_date(self):
        """
        对于生产robot，需要同步ntp时钟
        :return:
        """
        self.execute_command("mount -o remount ,rw / && timedatectl set-ntp true")
        time.sleep(1)
        date = self.execute_command("date")
        if date.status:
            return date.output
        else:
            raise RobotException(error_code=ErrorCode.PARAMIKO_EXECUTE_FAIL,
                                 detail="Update date fail \r\n" + date.error)

    @catch_exception(detail="Get date fail")
    def get_date(self) -> str:
        """
        获取Robot上的时间 in SZ
        :return:
        """
        date = self.execute_command("date")
        date_str = date.output
        dt = datetime.datetime.strptime(date_str, "%a %b %d %H:%M:%S UTC %Y")
        formatted = dt.strftime("%Y-%m-%d %H:%M:%S")
        return formatted

    @staticmethod
    def discover_flex() -> Dict[str, Dict[Any, Any]]:
        try:
            group = scan_flex()
            return group
        except ScanGatewayException:
            raise

    @catch_exception(detail="Create client fail")
    def create_client(self, host_name: str) -> uuid.UUID:
        client_id = uuid.uuid4()
        flex_client = FlexClientHandler.build_client(host_name)
        self.clients.update({str(client_id): flex_client})
        return client_id

    @catch_exception(detail="Get client fail")
    def get_client(self, client_id: str):
        client = self.clients.get(client_id, None)
        if client is None:
            raise exceptions.FlexHandlerException(
                error_code=error_code.ErrorCode.FLEX_HTTP_EXCEPTION,
                detail=f"Can not find client from {client_id}, Please create a new client")
        return client

    @catch_exception(detail="Move to fail")
    def move_to(self, client_id: str, mount: Mount, coordinate: dict[str, float], speed=567.8):
        client = self.get_client(client_id)
        client.maintenance_client.move_to(mount, point=Point(**coordinate), speed=speed)





if __name__ == '__main__':
    ...
